﻿using System;
using System.Collections.Generic;
using System.Common.Native;
using System.Diagnostics.CodeAnalysis;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Threading;

namespace System.Controls.Extensions
{
  [SuppressMessage( "ReSharper", "RedundantNameQualifier" )]
  public static class WindowEx
  {
    #region ...public methods...

    public static bool IsModal( this Window window )
    {
      return Convert.ToBoolean( _showingAsDialogField.GetValue( window ) );
    }

    public static bool IsDisposed( this Window window )
    {
      return Convert.ToBoolean( _disposedField.GetValue( window ) );
    }

    public static void ActivateEx( this Window window )
    {
      if ( window == null || window.IsDisposed() || !window.IsVisible )
        return;

      if ( window.Dispatcher.HasShutdownStarted )
        return;

      window.Dispatcher.BeginInvoke( DispatcherPriority.Normal, (Action)delegate
                                                                        {
                                                                          if ( window.IsDisposed() || !window.IsVisible )
                                                                            return;

                                                                          try
                                                                          {
                                                                            window.Activate();
                                                                            User.BringWindowToTop( window );
                                                                          }
                                                                          catch {}
                                                                        } );
    }

    public static void ForceFocus( this Window window, UIElement element = null )
    {
      if ( window == null || window.IsDisposed() || !window.IsVisible )
        return;

      User.SetWindowPos( window, HWND.TOP, 0, 0, 0, 0,
        SetWindowPosFlags.NOMOVE | SetWindowPosFlags.NOREPOSITION | SetWindowPosFlags.NOSIZE | SetWindowPosFlags.SHOWWINDOW );

      User.BringWindowToTop( window );
      User.SetForegroundWindow( window );

      if ( element == null )
        return;

      element.Dispatcher.BeginInvoke( (Action)delegate
                                              {
                                                element.Focus();
                                              }, DispatcherPriority.Input );
    }

    public static Rect GetRect( this System.Windows.Application app )
    {
      var window = System.Windows.Application.Current.MainWindow;
      return window.GetRect();
    }

    public static System.Windows.Point GetLocation( this Window window )
    {
      return new System.Windows.Point( window.Left, window.Top );
    }

    public static Rect GetRect( this Window window )
    {
      return new Rect( window.Left, window.Top, window.ActualWidth, window.ActualHeight );
    }

    public static System.Windows.Point TransferToScreen( this Window window, System.Windows.Point location )
    {
      var x = location.X;
      var y = location.Y;

      if ( x < 0 )
        x = 0;

      if ( y < 0 )
        y = 0;

      var screen = Screen.PrimaryScreen;
      if ( Screen.AllScreens.Length > 1 )
        screen = Screen.FromPoint( new Drawing.Point( (int)x, (int)y ) );

      while ( x + window.ActualWidth > screen.WorkingArea.Width && x - 1 > 0 )
        x--;

      while ( y + window.ActualHeight > screen.WorkingArea.Height && y - 1 > 0 )
        y--;

      return new System.Windows.Point( x, y );
    }

    public static System.Windows.Size TransferToScreen( this Window window, System.Windows.Size size )
    {
      var w = size.Width;
      var h = size.Height;

      if ( w < 0 )
        w = 0;

      if ( h < 0 )
        h = 0;

      if ( h <= 0 || w <= 0 )
        return size;

      var screen = Screen.PrimaryScreen;
      if ( Screen.AllScreens.Length > 1 )
        screen = Screen.FromPoint( new Drawing.Point( (int)window.Left, (int)window.Top ) );

      while ( w > screen.WorkingArea.Width && w - 1 > 0 )
        w--;

      while ( h > screen.WorkingArea.Height && h - 1 > 0 )
        h--;

      return new System.Windows.Size( w, h );
    }

    public static void SetWindowLocation( this Window window, System.Windows.Point location )
    {
      window.WindowStartupLocation = WindowStartupLocation.Manual;
      window.Top = location.Y;
      window.Left = location.X;
    }

    public static void SetLocation( this Window window, System.Windows.Point location )
    {
      var new_location = window.TransferToScreen( location );
      window.SetWindowLocation( new_location );
    }

    public static void SetWindowSize( this Window window, System.Windows.Size size )
    {
      if ( size.Height <= 0 || size.Width <= 0 )
        return;

      switch ( window.SizeToContent )
      {
      case SizeToContent.Manual:
        window.Width = size.Width;
        window.Height = size.Height;
        break;

      case SizeToContent.Width:
        window.Height = size.Height;
        break;

      case SizeToContent.Height:
        window.Width = size.Width;
        break;

      case SizeToContent.WidthAndHeight:
        break;
      }
    }

    public static void SetSize( this Window window, System.Windows.Size size )
    {
      var new_size = window.TransferToScreen( size );
      window.SetWindowSize( new_size );
    }

    public static void SetRect( this Window window, Rect rect )
    {
      var location = window.TransferToScreen( rect.Location );
      var size = window.TransferToScreen( rect.Size );

      var new_rect = new Rect( location, size );
      var win_rect = window.GetRect();

      if ( new_rect.Equals( win_rect ) )
        return;

      window.SetWindowLocation( location );
      window.SetWindowSize( size );
    }

    public static Window FindFirstModalChild( this Window window )
    {
      foreach ( var win in window.OwnedWindows.OfType<Window>() )
      {
        if ( win.IsModal() )
          return win;
      }

      return null;
    }

    public static Window FindApplicationLastModalWindow()
    {
      var windows = System.Windows.Application.Current.Windows.Cast<Window>().Reverse();

      foreach ( var window in windows )
      {
        if ( window.IsModal() )
          return window;
      }

      return null;
    }

    public static Rectangle AsRectangle( this Rect rect )
    {
      return new Rectangle( (int)rect.X, (int)rect.Y, (int)rect.Width, (int)rect.Height );
    }

    #endregion

    private static readonly Type _windowType = typeof( Window );

    private static readonly FieldInfo _showingAsDialogField = _windowType.GetField( "_showingAsDialog",
      BindingFlags.Instance | BindingFlags.NonPublic );

    private static readonly FieldInfo _disposedField = _windowType.GetField( "_disposed",
      BindingFlags.Instance | BindingFlags.NonPublic );
  }
}
